using System;
using System.IO;
using System.Security.Cryptography;
using System.Text.Json;

namespace ArchiVault.Core
{
    public static class ArchiveManager
    {
        public static void Create(string sourcePath, string outDir, ArchiveOptions? opt = null)
        {
            opt ??= new ArchiveOptions();
            Directory.CreateDirectory(outDir);

            // 1. 压缩/打包 计时
            var sw = System.Diagnostics.Stopwatch.StartNew();
            byte[] payload = File.GetAttributes(sourcePath).HasFlag(FileAttributes.Directory)
                          ? QuickZip.PackFolderParallel(sourcePath)
                          : QuickZip.ReadFileMemMap(sourcePath);   // ← 大文件零拷贝
            sw.Stop();
            long packMs = sw.ElapsedMilliseconds;

            // 2. 原长度 & MD5
            long originalLen = payload.Length;
            string srcMd5;
            using (var md5 = MD5.Create())
                srcMd5 = Convert.ToHexString(md5.ComputeHash(payload));

            // 3. 纠删分片 计时
            sw.Restart();
            ErasureCodec.SetParameters(opt.DataShards, opt.ParityShards);
            var shards = ErasureCodec.Encode(payload);
            sw.Stop();
            long encodeMs = sw.ElapsedMilliseconds;

            // 4. 写片 + 每片 MD5
            var shardMd5s = new string[shards.Length];
            for (int i = 0; i < shards.Length; i++)
            {
                var md5 = Convert.ToHexString(MD5.HashData(shards[i]));
                shardMd5s[i] = md5;
                File.WriteAllBytes(Path.Combine(outDir, $"shard.{i:D2}"), shards[i]);
            }

            // 5. 写 manifest（参数按顺序全部补齐）
            var mf = new Manifest(
                opt.DataShards,
                opt.ParityShards,
                shards[0].Length,
                originalLen,
                Path.GetFileName(sourcePath),
                srcMd5,
                shardMd5s,
                packMs,
                encodeMs);

            File.WriteAllText(Path.Combine(outDir, "manifest.json"),
                              JsonSerializer.Serialize(mf, new JsonSerializerOptions { WriteIndented = true }));

            // 6. 弹窗结果
            MessageBox.Show($"打包: {packMs} ms\n纠删: {encodeMs} ms\n总计: {packMs + encodeMs} ms");
        }

        public static void Restore(string shardsDir, string outPath)
        {
            // 1. 读 manifest
            var mf = JsonSerializer.Deserialize<Manifest>(
                File.ReadAllText(Path.Combine(shardsDir, "manifest.json")))!;

            // 2. 收集好片
            var shards = new byte[mf.DataShards][];
            for (int i = 0, found = 0; i < mf.DataShards + mf.ParityShards && found < mf.DataShards; i++)
            {
                string file = Path.Combine(shardsDir, $"shard.{i:D2}");
                if (!File.Exists(file)) continue;
                var bytes = File.ReadAllBytes(file);
                if (Convert.ToHexString(MD5.HashData(bytes)) != mf.ShardMd5s[i]) continue;
                shards[found++] = bytes;
            }
            if (shards.Any(s => s == null))
                throw new Exception("好片不足，无法还原");

            // 3. 纠删解码
            ErasureCodec.SetParameters(mf.DataShards, mf.ParityShards);
            byte[] payload = ErasureCodec.Decode(shards);
            Array.Resize(ref payload, (int)mf.OriginalLength);   // ← 把多余 0 切掉

            // 4. 整体 MD5 校验
            if (Convert.ToHexString(MD5.HashData(payload)) != mf.SourceMd5)
                throw new Exception("还原后 MD5 不符");

            // 5. 输出
            if (mf.SourceName.EndsWith(".zip"))
                QuickZip.UnpackToFolder(payload, outPath);
            else
                File.WriteAllBytes(outPath, payload);
        }
    }
}